package com.tencreat.server.handler;

import com.tencreat.common.core.utils.JwtService;
import com.tencreat.common.core.utils.SpringUtils;
import com.tencreat.common.entity.ImUser;
import com.tencreat.server.config.NettyConfig;
import com.tencreat.server.message.MessageSender;
import io.netty.channel.*;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;
import org.junit.platform.commons.util.StringUtils;

/**
 * TextWebSocketFrame 在netty中，用于为websocket专门处理文本的对象，frame是消息的载体
 */
@Slf4j
public class MessageHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    private MessageSender getMessageDealer() {
        return SpringUtils.getBean(MessageSender.class);
    }

    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame textWebSocketFrame) throws Exception {

        //获取客户端发送的消息
        String content = textWebSocketFrame.text();
        getMessageDealer().sendMsg(content);
//        ImUser user = getUser(ctx.channel());
//        log.info("接收到用户{}-{}的数据：{}",user.getUsername(), ctx.channel().id(), content);
    }

    /*登录校验*/
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        ImUser imUser = getUser(ctx);
        // 握手
        if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) {
            if (null == imUser) {
                //如果token校验不通过，发送连接关闭的消息给客户端，设置自定义code和msg用来区分下服务器是因为token不对才导致关闭
                ChannelFuture future = ctx.channel().writeAndFlush(new CloseWebSocketFrame(4001, "token 无效"))
                        .addListener(ChannelFutureListener.CLOSE).sync();
                if(future.isSuccess()){
                    log.info("【IM】ws客户端:{}登录校验失败！token无效!", ctx.channel().id());
                }
            } else {
                log.info("【IM】用户:{}登录校验成功,客户端:{}", imUser.getId(), ctx.channel().id());
                Channel channel = NettyConfig.getUserChannelMap().get(imUser.getId());
                //同用户多客户端登录,新登录的客户端强制退出之前的客户端
                if (null != channel && channel.isActive()) {
                    ChannelFuture future = channel.writeAndFlush(new CloseWebSocketFrame(4002, "您已再其他客户端登录"))
                            .addListener(ChannelFutureListener.CLOSE).sync();
                    if (future.isSuccess()) {
                        log.info("【IM】用户:{}, 强制退出其他客户端:{}", imUser.getId(), channel.id());
                        setChannelMapDeleted(channel);
                    }
                }
                //当客户端连接服务之后，获取客户端的channel，交给channelGroup来管理
                NettyConfig.getChannelGroup().add(ctx.channel());
                NettyConfig.getUserChannelMap().put(imUser.getId(), ctx.channel());
                // 登录注册
                getMessageDealer().connectActive(imUser);
            }
        }
        // 心跳
        else if (evt instanceof IdleStateEvent) {
            IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
            if (idleStateEvent.state() == IdleState.READER_IDLE) {
                ChannelFuture future = ctx.channel().close().sync();
                if(future.isSuccess()){
                    log.info("【IM】没有收到用户ID:{} 请求， 主动断开客户端:{}", imUser.getId(), ctx.channel().id());
                }
            } else if (idleStateEvent.state() == IdleState.WRITER_IDLE) {
                log.info("WRITER_IDLE!");
            } else if (idleStateEvent.state() == IdleState.ALL_IDLE) {
                log.info("ALL_IDLE!");
            }
        }
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        log.info("【IM】客户端-建立-链接!!!{}", ctx.channel().id());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        //当触发到handlerRemoved方法的时候，channelGroup会自动移除对应的channel，下面这句代码可不写
        //NettyConfig.getChannelGroup().remove(ctx.channel());
        if (ctx.channel().isActive() && !isChannelMapDeleted(ctx.channel())) {
            NettyConfig.getUserChannelMap().remove(getUser(ctx).getId());
        }
        log.info("【IM】客户端-断开-链接!!!{}", ctx.channel().id());
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
//        log.info("channelInactive");
        super.channelInactive(ctx);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
//        log.info("channelActive");
        super.channelActive(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.info("exceptionCaught");
        cause.printStackTrace();
    }

    private ImUser getUser(ChannelHandlerContext ctx) {
        // 握手事件， 校验token
        AttributeKey<String> attributeKey = AttributeKey.valueOf("token");
        //从通道中获取用户token
        String token = ctx.channel().attr(attributeKey).get();
        JwtService jwtService = SpringUtils.getBean(JwtService.class);
        return jwtService.getUser(token);
    }

    private ImUser getUser(Channel channel) {
        // 握手事件， 校验token
        AttributeKey<String> attributeKey = AttributeKey.valueOf("token");
        //从通道中获取用户token
        String token = channel.attr(attributeKey).get();
        JwtService jwtService = SpringUtils.getBean(JwtService.class);
        return jwtService.getUser(token);
    }

    private void setChannelMapDeleted(Channel channel) {
        AttributeKey<String> attributeKey = AttributeKey.valueOf("deleted");
        channel.attr(attributeKey).setIfAbsent("true");
    }

    private Boolean isChannelMapDeleted(Channel channel) {
        AttributeKey<String> attributeKey = AttributeKey.valueOf("deleted");
        String deleted = channel.attr(attributeKey).get();
        return StringUtils.isNotBlank(deleted) && deleted.equals("true");
    }
}
